#include <cstdlib>
#include <iostream>
#include "Melee.h"
Texture CMelee::UnitTexture;

int CMelee::MeleeCounter = 0;
CMelee::CMelee(State state)
	: random(100)
	, state(STATE_IDLE)
	, role(ROLE_ATTACK)
	, escChance(0)
{
	SetSize(20);
	SetMaxHealth(100);
	SetHealth(GetMaxHealth());
	SetDamage(10);
	SetDetRadius(GetSize() + 100);
	SetAtkRadius(GetSize() + 10);
	SetAttackRate(1.0f);
	SetSpeed(30.0f);
	Target = NULL;
	attacktime = 0;
	str = "IDLE";
	roles = "ATTACK";
	LoadTGA(&UnitTexture, "bin/warrior.tga");
	RandLoc(temploc);
	temphealth = this->GetHealth();
	//mb = new MessageBoard();
	MeleeCounter++;
	MeleeCount = MeleeCounter;
}

CMelee::~CMelee()
{

}

void CMelee::RandLoc(Vector3D &temp)
{
	temp.x = rand() % 800;
	temp.y = rand() % 600;
	temp.z = 0;
}

void CMelee::ChangeState()
{
	if (role == ROLE_ATTACK)
	{
		// if else statement
		if (state == STATE_IDLE)
		{
			random = rand() % 100;
			if (random > 50)
			{
				state = STATE_ROAM;
			}
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				//if (Target->GetActive() && Target->GetTeam() != this->GetTeam())
				//{
				if (((this->GetDetRadius() + Target->GetSize())*(this->GetDetRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHASE;
				}
				//}
				if (this->GetHealth() < temphealth)
				{
					state = STATE_CHASE;
				}
			}
		}
		else if (state == STATE_ROAM)
		{
			if (Target != NULL)
			{
				if (Target->GetActive() && Target->GetTeam() != this->GetTeam())
				{
					Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
					if (((this->GetDetRadius() + Target->GetSize())*(this->GetDetRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
					{
						state = STATE_CHASE;
					}
				}
				if ((temploc - this->GetPos()).LengthSquared() < 1) // If reach
				{
					state = STATE_IDLE;
					RandLoc(temploc); // Random location
				}
				if (this->GetHealth() < temphealth)
				{
					state = STATE_CHASE;
				}
			}
		}
		else if (state == STATE_CHASE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				//if (Target->GetActive() && Target->GetTeam() != this->GetTeam())
				//{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				if (((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_ATTACK;
				}
				//}
			}
			else
			{
				temphealth = this->GetHealth();
				state = STATE_IDLE;
			}
		}
		else if (state == STATE_ATTACK)
		{
			escChance = rand() % 10;
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				if (this->GetHealth() < 20 && escChance < 4)
				{
					state = STATE_FLEE;
				}
				//if (Target->GetActive() && Target->GetTeam() != this->GetTeam())  // distance 
				//{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				if (((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) < (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHASE;
				}
				//}

			}
			else
			{
				temphealth = this->GetHealth();
				state = STATE_IDLE;
			}

		}
		else if (state == STATE_FLEE)
		{
			Vector3D CurrentTarget = this->GetPos() - Target->GetPos();
			if (((Target->GetDetRadius() + 50)*(Target->GetDetRadius() + 50)) < (CurrentTarget.Dot(CurrentTarget)))
			{
				temphealth = this->GetHealth();
				state = STATE_IDLE;
			}
		}
	}
	else if (role == ROLE_ASSIST)
	{
		if (state == STATE_GIVESHIELD)
		{
			Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
			if (((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
			{
				if (CRange *range = dynamic_cast<CRange*>(Target))
				{
					if (range->GetShield() < range->GetFullShield())
					{
						range->ShieldTo(this);
					}
					else
					{
						state = STATE_IDLE;
						role = ROLE_ATTACK;
					}
				}
			}
		}
	}
}

void CMelee::Respond(float Time)
{
	// switch case
	switch (state)
	{
	case CMelee::STATE_IDLE:
		{
			str = "IDLE";
		}
		break;
	case CMelee::STATE_ROAM:
		{
			str = "ROAM";
			this->SetSpeed(30.0f);
			Vector3D path = temploc - this->GetPos();
			this->SetPos(this->GetPos() + (path.Normalized() * Time)*this->GetSpeed());
		}
		break;
	case CMelee::STATE_CHASE:
		{
			str = "CHASE";
			this->SetSpeed(30.0f * 2);
			temploc = Target->GetPos();
			Vector3D path = temploc - this->GetPos();
			this->SetPos(this->GetPos() + (path.Normalized() * Time)*this->GetSpeed());
		}
		break;
	case CMelee::STATE_ATTACK:
		{
			str = "ATTACK";
			attacktime+=Time;
			Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
			if (attacktime >= this->GetAttackRate())
			{
				if (((this->GetAtkRadius() + 50)*(this->GetAtkRadius() + 50)) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					// Target->SetHealth(Target->GetHealth() - this->GetDamage());
					Target->DealDamageTo(this);
					attacktime = 0;
				}
			}
		}
		break;
	case CMelee::STATE_FLEE:
		{
			str = "FLEE";
			this->SetSpeed(30.0f * 1.5);
			temploc = Target->GetPos() - this->GetPos();
			this->SetPos(this->GetPos() - (temploc.Normalized() * Time)*this->GetSpeed());
		}
		break;
	case CMelee::STATE_GIVESHIELD:
		{
			str = "GIVE SHIELD";
			this->SetSpeed(30.0f * 1.5);
			Vector3D path = Target->GetPos() - this->GetPos();
			this->SetPos(this->GetPos() + (path.Normalized() * Time) * this->GetSpeed());
		}
		break;
	default:
		break;
	}

	switch (role)
	{
	case CMelee::ROLE_ATTACK:
		roles = "ATTACK";
		break;
	case CMelee::ROLE_ASSIST:
		roles = "ASSIST";
		break;
	default: 
		break;
	}
}

void CMelee::Update(float Time)
{
	static float count = 0; // Initialise Counter
	count += Time;

	if (count > 0.1f)
	{
		ChangeState(); // Change state
		count = 0; // Reset Counter
	}
	Respond(Time); // Respond
	//SetMessage();
}

void CMelee::Render(void)
{
	glEnable(GL_BLEND);
	if (this->GetToggle())
	{
		// Detection radius
		glPushMatrix();
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1.0f, 1.0f, 0.0f, 0.2f);
		glTranslatef(GetPos().x, GetPos().y, GetPos().z);
		glScalef(GetDetRadius(), GetDetRadius(), 1);
		glutSolidSphere(1, 40, 40);
		glPopMatrix();

		glPushMatrix();
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1.0f, 0.0f, 0.0f, 0.2f);
		glTranslatef(GetPos().x, GetPos().y, GetPos().z);
		glScalef(GetAtkRadius(), GetAtkRadius(), 1);
		glutSolidSphere(1, 40, 40);
		glPopMatrix();
	}

	glEnable(GL_TEXTURE_2D);
	glPushMatrix();
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBindTexture(GL_TEXTURE_2D, UnitTexture.texID);
	glColor3f(1.0f, 1.0f, 1.0f);
	glTranslatef(GetPos().x, GetPos().y, GetPos().z);
	glScalef(GetSize(), GetSize(), 1);

	glBegin(GL_QUADS);
	glTexCoord2f(0, 0); glVertex2f(-2, -2);
	glTexCoord2f(1, 0); glVertex2f(2, -2);
	glTexCoord2f(1, 1); glVertex2f(2, 2);
	glTexCoord2f(0, 1); glVertex2f(-2, 2);
	glEnd();

	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_BLEND);

	RenderHealthBar();
	//PrintMessage(GetPos().x, GetPos().y, mb->RcvMessage().c_str());
	PrintState(GetPos().x - 55, GetPos().y + 40, 1, 1, 1, "STATE: ");
	PrintState(GetPos().x + 10, GetPos().y + 40, 1, 1, 1, str);
	string UnitCounter = "Melee "  + to_string(static_cast<long long>(MeleeCount));
	char* UnitCounterC = new char[UnitCounter.length() + 1];
	strcpy(UnitCounterC, UnitCounter.c_str());
	PrintState(GetPos().x - GetSize(), GetPos().y + GetSize() + 40, GetColor().x, GetColor().y, GetColor().z, UnitCounterC);
	PrintState(GetPos().x - 65, GetPos().y - 40, 1, 1, 1, "ROLE: ");
	PrintState(GetPos().x - 10, GetPos().y - 40, 1, 1, 1, roles);
}

void CMelee::UnitInteraction(CUnit* go_Unit)
{
	if (CArrow* test = dynamic_cast<CArrow*>(go_Unit))
	{
		return;
	}
	if (role == ROLE_ATTACK)
	{
		if (this->GetTeam() != go_Unit->GetTeam())
		{
			if (CUnit* test = dynamic_cast<CUnit*>(go_Unit))
			{
				if (go_Unit == Target || state == STATE_GIVESHIELD)
					return;
				else if (Target == NULL || !Target->GetActive() || Target->GetTeam() == this->GetTeam())
				{
					Target = go_Unit;
					return;
				}
				else
				{
					Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
					Vector3D ThisTarget = go_Unit->GetPos() - this->GetPos();

					if (ThisTarget.Dot(ThisTarget) < CurrentTarget.Dot(CurrentTarget))
					{
						Target = go_Unit;
					}
				}
			}
		}
	}
	else if (role == ROLE_ASSIST)
	{
		if (this->GetTeam() == go_Unit->GetTeam())
		{
			if (CRange* range = dynamic_cast<CRange*>(go_Unit))
			{
				if (range == Target)
					return;
				else if (Target == NULL || !Target->GetActive())
				{
					Target = range;
					return;
				}
				else if (state == STATE_GIVESHIELD)
				{
					if (CRange* range2 = dynamic_cast<CRange*>(Target))
					{
						if (range2->GetShield() > range->GetShield())
						{
							range2 = range;
						}
					}
				}
			}
		}
	}
}

void CMelee::PrintState(int x, int y, float r, float g, float b, char *str)
{
	va_list args;	//  Variable argument list
	int len;		//	String length
	int i;			//  Iterator
	char * text;	//	Text

	//  Initialize a variable argument list
	va_start(args, str);

	//  Return the number of characters in the string referenced the list of arguments.
	//  _vscprintf doesn't count terminating '\0' (that's why +1)
	len = _vscprintf(str, args) + 1;

	//  Allocate memory for a string of the specified size
	text = (char *)malloc(len * sizeof(char));

	//  Write formatted output using a pointer to the list of arguments
	vsprintf_s(text, len, str, args);

	//  End using variable argument list 
	va_end(args);

	glColor3f(r, g, b);
	//  Specify the raster position for pixel operations.
	glRasterPos3f(x, y, 0);


	//  Draw the characters one by one
	for (i = 0; text[i] != '\0'; i++)
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]);

	//  Free the allocated memory for the string
	free(text);
}

void CMelee::PrintMessage(int x, int y, char *str)
{
	PrintState(x - 80, y + 80, 1.0f, 1.0f, 1.0f, "MESSAGE: ");
	PrintState(x + 20, y + 80, 1.0f, 1.0f, 1.0f, str);
}

void CMelee::DealDamageTo(CUnit* go_Unit)
{
	if(CArrow* arrow = dynamic_cast<CArrow*>(go_Unit))
	{
		if(arrow->state == CArrow ::ARROW_DAMAGE)
			this->SetHealth(this->GetHealth() - go_Unit->GetDamage());
	}
	else
	{
		this->SetHealth(this->GetHealth() - go_Unit->GetDamage());
	}
}

int CMelee::GetMeleeCount(void)
{
	return MeleeCount;
}

void CMelee::CheckMessage(std::vector<MessageBoard*> &Msgb)
{
	for (int i = 0; i < Msgb.size(); i++)
	{
		if (Msgb[i]->Getto() == this)
			return;
	}

	for (int i = 0; i < Msgb.size(); i++)
	{
		if (Msgb[i]->Getfrom()->GetTeam() == this->GetTeam())
		{
			if (Msgb[i]->Getsearchfor() == "MELEE" && Msgb[i]->Getto() == NULL)
			{
				if (Msgb[i]->Getfrom() != this && GetCurrentMsg()->Getmsg() == "NULL" && (this->state == State::STATE_ROAM || this->state == State::STATE_IDLE))
				{
					Target = Msgb[i]->Getfrom();
					if (Msgb[i]->Getmsg() == "REPLACE ME")
					{
						role = Role::ROLE_ATTACK;
						state = State::STATE_CHASE;
					}
					else if (Msgb[i]->Getmsg() == "NEED REINFORCEMENT")
					{
						role = Role::ROLE_ATTACK;
						state = State::STATE_CHASE;
					}
					else if (Msgb[i]->Getmsg() == "NEED SHIELD")
					{
						role = Role::ROLE_ASSIST;
						state = State::STATE_GIVESHIELD;
					}
				}
			}
		}
	}
}

void CMelee::HealTo(CUnit* go_Unit)
{
	if(CArrow* arrow = dynamic_cast<CArrow*>(go_Unit))
	{
		if(arrow->state == CArrow ::ARROW_HEAL)
		{
			this->SetHealth(this->GetHealth() + go_Unit->GetDamage());

			if(GetHealth() > GetMaxHealth())
				this->SetHealth(GetMaxHealth());
		}
	}
}